﻿using System;
using System.Collections;
using System.Data.SQLite;
using System.Text;
using CompactMapper.Orm.Contracts;
using CompactMapper.Orm.Extensions;
using CompactMapper.Orm.Queries;
using CompactMapper.Orm.UnitOfWork.Commands.Implementations;

namespace CompactMapper.Orm.UnitOfWork.Mappings
{
    internal sealed class LinesReader : IEnumerable, IDisposable
    {
        private readonly SQLiteConnection _connection;
        private readonly Table _table;
        private SQLiteDataReader _reader;
        private readonly SQLiteDataReader _relationReader;

        private LinesReader(SQLiteConnection connection, Table table)
        {
            _connection = connection;
            _table = table;
        }

        internal LinesReader(SQLiteConnection connection, Table table, Query query, TablesTree tablesTreeRoot)
            : this(connection, table)
        {
            _reader = table.QueryCommand.Run(connection, query, tablesTreeRoot);
        }


        internal LinesReader(SQLiteConnection connection, Table table, IKey key)
            : this(connection, table)
        {
            _reader = table.GetByKeyCommand.Run(connection, key);
        }


        internal LinesReader(SQLiteConnection connection, Table table, String parentTableName, IKey parentTableKey)
            : this(connection, table)
        {
            _relationReader = CreateJoinTableReader(_table, parentTableName, parentTableKey);
        }


        private SQLiteDataReader CreateJoinTableReader(Table table, String parentTableName, IKey parentTableKey)
        {
            var command = new SQLiteCommand(_connection);
            var joinTableName = parentTableName + table.Name;
            var clause = String.Format("{0} = {1}", parentTableName, parentTableName.ToSqlValue());
            var builder = new SqlBuilder().Query(joinTableName, new[] { clause });
            command.CommandText = builder.ToString();
            command.Parameters.Add(new SQLiteParameter(parentTableName, parentTableKey.Value));
            return command.ExecuteReader();
        }


        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        private IEnumerator GetEnumerator()
        {
            if (_relationReader != null)
            {
                while (_relationReader.Read())
                {
                    var command = new SQLiteCommand(_connection);
                    var builder =
                        new StringBuilder("SELECT * FROM ").Append(_table.Name).Append(" WHERE [Key]=@key");
                    command.CommandText = builder.ToString();
                    command.Parameters.Add(new SQLiteParameter("@key", _relationReader[_table.Name]));
                    _reader = command.ExecuteReader();
                    while (_reader.Read())
                    {
                        yield return ReadLine(_reader, _table);
                    }
                }
            }
            else
            {
                while (_reader.Read())
                {
                    yield return ReadLine(_reader, _table);
                }
            }
        }

        private static object ReadLine(SQLiteDataReader reader, Table table)
        {
            var entity = table.Meta.Create();
            foreach (var column in table.Meta.Columns)
            {
                var dbValue = reader[column.Name];
                var value = column.FromSqLiteObject(dbValue);
                column.SetValue(entity, value);
            }
            return entity;
        }

        public void Dispose()
        {
            if (_relationReader != null)
                _relationReader.Dispose();
            if (_reader != null)
                _reader.Dispose();
        }
    }
}